from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Dict, Optional
from ..db import get_db
from .. import models, schemas
from ..security import get_current_user

router = APIRouter()

def _max_order(db: Session, owner_id: int, parent_id: Optional[int]) -> int:
    q = db.query(models.Folder.order).filter(models.Folder.owner_id == owner_id)
    if parent_id is None:
        q = q.filter(models.Folder.parent_id == None)  # noqa: E711
    else:
        q = q.filter(models.Folder.parent_id == parent_id)
    row = q.order_by(models.Folder.order.desc()).first()
    return (row[0] if row else -1)

def _is_descendant(db: Session, owner_id: int, possible_ancestor_id: int, target_id: int) -> bool:
    nodes = db.query(models.Folder).filter(models.Folder.owner_id == owner_id).all()
    table: Dict[int, list[int]] = {}
    for n in nodes:
        table.setdefault(n.parent_id or 0, []).append(n.id)
    stack = table.get(possible_ancestor_id, [])[:]
    while stack:
        nid = stack.pop()
        if nid == target_id:
            return True
        stack.extend(table.get(nid, []))
    return False

def _ensure_inbox(db: Session, owner_id: int):
    inbox = db.query(models.Folder).filter(models.Folder.owner_id == owner_id, models.Folder.name == "收集箱").first()
    if not inbox:
        # place on top by using a very small order
        inbox = models.Folder(name="收集箱", parent_id=None, owner_id=owner_id, order=-1)
        db.add(inbox)
        db.commit()
        db.refresh(inbox)
    return inbox

@router.post("/", response_model=schemas.FolderOut)
def create_folder(payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    next_order = _max_order(db, current.id, payload.parent_id) + 1
    f = models.Folder(name=payload.name, parent_id=payload.parent_id, owner_id=current.id, order=next_order)
    db.add(f)
    db.commit()
    db.refresh(f)
    return f

# alias without trailing slash to avoid 307 redirect
@router.post("", response_model=schemas.FolderOut)
def create_folder_noslash(payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    return create_folder(payload, db, current)

@router.get("/", response_model=List[schemas.FolderOut])
def list_folders(db: Session = Depends(get_db), current=Depends(get_current_user)):
    _ensure_inbox(db, current.id)
    return db.query(models.Folder).filter(models.Folder.owner_id == current.id).order_by(models.Folder.parent_id, models.Folder.order, models.Folder.id).all()

# alias without trailing slash to avoid 307 redirect
@router.get("", response_model=List[schemas.FolderOut])
def list_folders_noslash(db: Session = Depends(get_db), current=Depends(get_current_user)):
    return list_folders(db, current)

@router.get("/tree")
def tree(db: Session = Depends(get_db), current=Depends(get_current_user)):
    _ensure_inbox(db, current.id)
    items = db.query(models.Folder).filter(models.Folder.owner_id == current.id).order_by(models.Folder.parent_id, models.Folder.order, models.Folder.id).all()
    # counts of issues per folder (only current user's issues)
    counts_rows = (
        db.query(models.IssueFolder.folder_id, func.count(models.IssueFolder.id))
        .join(models.Issue, models.Issue.id == models.IssueFolder.issue_id)
        .filter(models.Issue.owner_id == current.id)
        .filter(models.Issue.is_deleted == False)
        .group_by(models.IssueFolder.folder_id)
        .all()
    )
    counts: Dict[int, int] = {fid: cnt for fid, cnt in counts_rows}
    nodes: Dict[int, dict] = {f.id: {"id": f.id, "name": f.name, "parent_id": f.parent_id, "count": counts.get(f.id, 0), "children": []} for f in items}
    roots = []
    for f in items:
        if f.parent_id and f.parent_id in nodes:
            nodes[f.parent_id]["children"].append(nodes[f.id])
        else:
            roots.append(nodes[f.id])
    # aggregate counts to include descendants
    def dfs_sum(n: dict) -> int:
        total = int(n.get("count", 0))
        for c in n.get("children", []):
            total += dfs_sum(c)
        n["count"] = total
        return total
    for r in roots:
        dfs_sum(r)
    return roots

@router.put("/{folder_id}", response_model=schemas.FolderOut)
def update_folder(folder_id: int, payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.Folder).filter(models.Folder.id == folder_id, models.Folder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    f.name = payload.name
    f.parent_id = payload.parent_id
    db.commit()
    db.refresh(f)
    return f

@router.delete("/{folder_id}")
def delete_folder(folder_id: int, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.Folder).filter(models.Folder.id == folder_id, models.Folder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    # also delete links
    db.query(models.IssueFolder).filter(models.IssueFolder.folder_id == folder_id).delete()
    db.delete(f)
    db.commit()
    return {"ok": True}

@router.patch("/reorder/{folder_id}", response_model=schemas.FolderOut)
def reorder_folder(folder_id: int, body: schemas.FolderReorder, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.Folder).filter(models.Folder.id == folder_id, models.Folder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    # Fixed Inbox folder named '收集箱'
    inbox = db.query(models.Folder).filter(models.Folder.owner_id == current.id, models.Folder.name == "收集箱").first()
    if inbox:
        # Inbox itself cannot be moved or reparented
        if f.id == inbox.id:
            raise HTTPException(status_code=400, detail="收集箱不可移动")
        # Others cannot be placed before Inbox at root level
        if body.before_id and body.before_id == inbox.id and (body.parent_id is None):
            raise HTTPException(status_code=400, detail="收集箱固定置顶，不能将目录移到其上方")
    new_parent = body.parent_id
    if new_parent == folder_id:
        raise HTTPException(status_code=400, detail="Cannot set folder as its own parent")
    if new_parent and _is_descendant(db, current.id, folder_id, new_parent):
        raise HTTPException(status_code=400, detail="Cannot move folder under its descendant")

    # Move to new parent
    f.parent_id = new_parent

    # Determine new order
    # If before_id provided
    if body.before_id:
        before = db.query(models.Folder).filter(models.Folder.id == body.before_id, models.Folder.owner_id == current.id).first()
        if not before:
            raise HTTPException(status_code=404, detail="before_id not found")
        if before.parent_id != new_parent:
            raise HTTPException(status_code=400, detail="before_id must be sibling in target parent")
        # shift siblings
        siblings_q = db.query(models.Folder).filter(models.Folder.owner_id == current.id)
        if new_parent is None:
            siblings_q = siblings_q.filter(models.Folder.parent_id == None)  # noqa: E711
        else:
            siblings_q = siblings_q.filter(models.Folder.parent_id == new_parent)
        siblings_q = siblings_q.filter(models.Folder.order >= before.order)
        siblings_q.update({models.Folder.order: models.Folder.order + 1}, synchronize_session=False)
        f.order = before.order
    elif body.after_id:
        after = db.query(models.Folder).filter(models.Folder.id == body.after_id, models.Folder.owner_id == current.id).first()
        if not after:
            raise HTTPException(status_code=404, detail="after_id not found")
        if after.parent_id != new_parent:
            raise HTTPException(status_code=400, detail="after_id must be sibling in target parent")
        f.order = after.order + 1
        siblings_q = db.query(models.Folder).filter(models.Folder.owner_id == current.id)
        if new_parent is None:
            siblings_q = siblings_q.filter(models.Folder.parent_id == None)  # noqa: E711
        else:
            siblings_q = siblings_q.filter(models.Folder.parent_id == new_parent)
        siblings_q = siblings_q.filter(models.Folder.order >= f.order)
        siblings_q.update({models.Folder.order: models.Folder.order + 1}, synchronize_session=False)
    else:
        # append to end
        f.order = _max_order(db, current.id, new_parent) + 1

    db.commit()
    db.refresh(f)
    return f
